<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Blockly Demo: Graph</title>
  <script src="https://www.google.com/jsapi"></script>
  <script src="../../blockly_compressed.js"></script>
  <script src="../../blocks_compressed.js"></script>
  <script src="../../javascript_compressed.js"></script>
  <script src="../../msg/js/en.js"></script>
  <style>
    body {
      background-color: #fff;
      font-family: sans-serif;
    }
    h1 {
      font-weight: normal;
      font-size: 140%;
    }
    #funcText {
      margin-top: 1em;
      margin-left: 1.5em;
      font-family: sans-serif;
    }
    #funcText>img {
      height: 3px;
      width: 15px;
      vertical-align: middle;
      margin-right: .5em;
    }
    #y1 {
      background-color: #36c;
    }
  </style>
</head>
<body>
  <h1><a href="https://developers.google.com/blockly/">Blockly</a> &gt;
    <a href="../index.html">Demos</a> &gt; Graph</h1>

  <p>This is a demo of giving instant feedback as blocks are changed.</p>

  <p>&rarr; More info on <a href="https://developers.google.com/blockly/guides/configure/web/code-generators#generating_code">Realtime generation</a>&hellip;</p>

  <table>
    <tr>
      <td>
        <div id="visualization" style="width: 400px"></div>
      </td>
      <td>
        <div id="blocklyDiv" style="height: 400px"></div>
      </td>
    </tr>
  </table>

  <div id="funcText">
    <img id="y1" src="../../media/1x1.gif">
    ...
  </div>

  <xml id="toolbox" style="display: none">
    <category name="Math">
      <block type="math_number"></block>
      <block type="math_arithmetic">
        <value name="A">
          <shadow type="math_number">
            <field name="NUM">1</field>
          </shadow>
        </value>
        <value name="B">
          <shadow type="math_number">
            <field name="NUM">1</field>
          </shadow>
        </value>
      </block>
      <block type="math_single">
        <value name="NUM">
          <shadow type="math_number">
            <field name="NUM">9</field>
          </shadow>
        </value>
      </block>
      <block type="math_trig">
        <value name="NUM">
          <shadow type="math_number">
            <field name="NUM">45</field>
          </shadow>
        </value>
      </block>
      <block type="math_constant"></block>
      <block type="math_number_property">
        <value name="NUMBER_TO_CHECK">
          <shadow type="math_number">
            <field name="NUM">0</field>
          </shadow>
        </value>
      </block>
      <block type="math_round">
        <value name="NUM">
          <shadow type="math_number">
            <field name="NUM">3.1</field>
          </shadow>
        </value>
      </block>
      <block type="math_modulo">
        <value name="DIVIDEND">
          <shadow type="math_number">
            <field name="NUM">64</field>
          </shadow>
        </value>
        <value name="DIVISOR">
          <shadow type="math_number">
            <field name="NUM">10</field>
          </shadow>
        </value>
      </block>
      <block type="math_constrain">
        <value name="VALUE">
          <shadow type="math_number">
            <field name="NUM">50</field>
          </shadow>
        </value>
        <value name="LOW">
          <shadow type="math_number">
            <field name="NUM">1</field>
          </shadow>
        </value>
        <value name="HIGH">
          <shadow type="math_number">
            <field name="NUM">100</field>
          </shadow>
        </value>
      </block>
      <block type="math_random_int">
        <value name="FROM">
          <shadow type="math_number">
            <field name="NUM">1</field>
          </shadow>
        </value>
        <value name="TO">
          <shadow type="math_number">
            <field name="NUM">100</field>
          </shadow>
        </value>
      </block>
      <block type="math_random_float"></block>
    </category>
    <category name="Variables">
      <block type="graph_get_x"></block>
    </category>
    <category name="Logic">
      <block type="logic_compare"></block>
      <block type="logic_operation"></block>
      <block type="logic_negate"></block>
      <block type="logic_boolean"></block>
      <block type="logic_ternary"></block>
    </category>
  </xml>

  <xml id="startBlocks" style="display: none">
    <block type="graph_set_y" deletable="false" x="100" y="100">
      <value name="VALUE">
        <block type="math_arithmetic">
          <field name="OP">POWER</field>
          <value name="A">
            <block type="graph_get_x"></block>
            <shadow type="math_number">
              <field name="NUM">1</field>
            </shadow>
          </value>
          <value name="B">
            <block type="math_number">
              <field name="NUM">2</field>
            </block>
            <shadow type="math_number">
              <field name="NUM">1</field>
            </shadow>
          </value>
        </block>
      </value>
    </block>
  </xml>

  <script>
// Load the Google Chart Tools Visualization API and the chart package.
if (typeof google == 'object') {
  google.load('visualization', '1', {packages: ['corechart']});
} else {
  alert('Unable to load Google\'s chart API.\n' +
        'Are you connected to the Internet?');
}

// Define the custom blocks and their JS generators.
Blockly.Blocks['graph_get_x'] = {
  // x variable getter.
  init: function() {
    this.jsonInit({
      "message0": "x",
      "output": "Number",
      "colour": Blockly.Blocks.variables.HUE,
      "tooltip": Blockly.Msg.VARIABLES_GET_TOOLTIP,
      "helpUrl": Blockly.Msg.VARIABLES_GET_HELPURL
    });
  }
};

Blockly.JavaScript['graph_get_x'] = function(block) {
  // x variable getter.
  return ['x', Blockly.JavaScript.ORDER_ATOMIC];
};

Blockly.Blocks['graph_set_y'] = {
  // y variable setter.
  init: function() {
    this.jsonInit({
      "message0": "y = %1",
      "args0": [
        {
          "type": "input_value",
          "name": "VALUE",
          "check": "Number"
        }
      ],
      "colour": Blockly.Blocks.variables.HUE,
      "tooltip": Blockly.Msg.VARIABLES_SET_TOOLTIP,
      "helpUrl": Blockly.Msg.VARIABLES_SET_HELPURL
    });
  }
};

Blockly.JavaScript['graph_set_y'] = function(block) {
  // y variable setter.
  var argument0 = Blockly.JavaScript.valueToCode(block, 'VALUE',
      Blockly.JavaScript.ORDER_ASSIGNMENT) || '';
  return 'y = ' + argument0 + ';';
};

/**
 * Create a namespace for the application.
 */
var Graph = {};

/**
 * Main Blockly workspace.
 * @type {Blockly.WorkspaceSvg}
 */
Graph.workspace = null;

/**
 * Cached copy of the function string.
 * @type {?string}
 * @private
 */
Graph.oldFormula_ = null;

/**
 * Drawing options for the Chart API.
 * @type {!Object}
 * @private
 */
Graph.options_ = {
  //curveType: 'function',
  width: 400, height: 400,
  chartArea: {left: '10%', width: '85%', height: '85%'}
};

/**
 * Visualize the graph of y = f(x) using Google Chart Tools.
 * For more documentation on Google Chart Tools, see this linechart example:
 * https://developers.google.com/chart/interactive/docs/gallery/linechart
 */
Graph.drawVisualization = function() {
  var formula = Blockly.JavaScript.workspaceToCode(Graph.workspace);
  if (formula === Graph.oldFormula_) {
    // No change in the formula, don't recompute.
    return;
  }
  Graph.oldFormula_ = formula;

  // Create and populate the data table.
  var data = google.visualization.arrayToDataTable(Graph.plot(formula));
  // Create and draw the visualization, passing in the data and options.
  new google.visualization.LineChart(document.getElementById('visualization')).
      draw(data, Graph.options_);

  // Create the "y = ..." label.  Find the relevant part of the code.
  formula = formula.substring(formula.indexOf('y = '));
  formula = formula.substring(0, formula.indexOf(';'));
  var funcText = document.getElementById('funcText');
  funcText.replaceChild(document.createTextNode(formula), funcText.lastChild);
};

/**
 * Plot points on the function y = f(x).
 * @param {string} code JavaScript code.
 * @return {!Array.<!Array>} 2D Array of points on the graph.
 */
Graph.plot = function(code) {
  // Initialize a table with two column headings.
  var table = [];
  var y;
  // TODO: Improve range and scale of graph.
  for (var x = -10; x <= 10; x = Math.round((x + 0.1) * 10) / 10) {
    try {
      eval(code);
    } catch (e) {
      y = NaN;
    }
    if (!isNaN(y)) {
      // Prevent y from being displayed inconsistently, some in decimals, some
      // in scientific notation, often when y has accumulated rounding errors.
      y = Math.round(y * Math.pow(10, 14)) / Math.pow(10, 14);
      table.push([x, y]);
    }
  }
  // Add column heading to table.
  if (table.length) {
    table.unshift(['x', 'y']);
  } else {
    // If the table is empty, add a [0, 0] row to prevent graph error.
    table.unshift(['x', 'y'], [0, 0]);
  }
  return table;
};

/**
 * Force Blockly to resize into the available width.
 */
Graph.resize = function() {
  var width = Math.max(window.innerWidth - 440, 250);
  document.getElementById('blocklyDiv').style.width = width + 'px';
  Blockly.svgResize(Graph.workspace);
};

/**
 * Initialize Blockly and the graph.  Called on page load.
 */
Graph.init = function() {
  Graph.workspace = Blockly.inject('blocklyDiv',
      {collapse: false,
       disable: false,
       media: '../../media/',
       toolbox: document.getElementById('toolbox')});
  Blockly.Xml.domToWorkspace(document.getElementById('startBlocks'),
                             Graph.workspace);
  Graph.workspace.clearUndo();

  // When Blockly changes, update the graph.
  Graph.workspace.addChangeListener(Graph.drawVisualization);
  Graph.workspace.addChangeListener(Blockly.Events.disableOrphans);
  Graph.resize();
};

window.addEventListener('load', Graph.init);
window.addEventListener('resize', Graph.resize);
  </script>

</body>
</html>
